package com.fengxu.gamescript.ui.edit

import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import android.text.Editable
import android.util.Base64
import android.view.*
import android.widget.*
import androidx.core.widget.doOnTextChanged
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.fengxu.gamescript.R
import com.fengxu.gamescript.custom.ScaleListView
import com.fengxu.gamescript.data.GlobalData
import com.fengxu.gamescript.data.ScriptRunNumChangeType
import com.fengxu.gamescript.jsengine.core.JsRuntime
import com.fengxu.gamescript.ui.base.BaseActivity
import com.fengxu.gamescript.ui.edit.core.CodePane
import com.fengxu.gamescript.ui.edit.core.PreformEdit
import com.fengxu.gamescript.ui.log.LoggerActivity
import com.fengxu.gamescript.util.CommonUtils
import com.fengxu.script.JsEngine
import com.xuexiang.xui.utils.DensityUtils
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
import com.xuexiang.xui.widget.popupwindow.bar.CookieBar
import com.xuexiang.xui.widget.toast.XToast
import org.jetbrains.anko.*
import org.jetbrains.anko.sdk27.coroutines.onClick
import java.io.File
import kotlin.properties.Delegates


class EditActivity : BaseActivity() {

    // 编辑页标题
    private val editTitle: TextView by lazy {
        findViewById<TextView>(R.id.edit_title)
    }

    // 运行/停止按钮
    private val runScriptBtn: ImageButton by lazy {
        findViewById(R.id.edit_state)
    }

    // 代码面板
    private val codePane:CodePane by lazy {
        findViewById(R.id.code_edit)
    }

    // 代码提示列表
    private val codeTipRecyclerView:RecyclerView by lazy {
        findViewById<RecyclerView>(R.id.edit_recyclerCodeTip)
    }

    // 撤回操作编辑器
    private var preformEdit: PreformEdit by Delegates.notNull()

    // 脚本文件对象
    private var scriptFile: File by Delegates.notNull()

    // 用户是否保存文件
    private var isSaveFile:Boolean = true

    // code提示的开始索引
    private var mCodeTipStartIndex = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        onNewIntent(intent)
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        setContentView(R.layout.activity_edit)
        scriptFile = File(intent.data!!.path)
        initView()
    }


    /**
     * 初始化控件
     */
    private fun initView() {
        // 设置标题
        editTitle.text = scriptFile.name
        // 读取脚本中的代码
        loadScript()
        // 退出事件
        findViewById<View>(R.id.edit_back).onClick { backEditor() }
        // 初始化符号快捷栏
        initSymbolList()
        // 初始化撤回与反撤回
        findViewById<View>(R.id.edit_recall).onClick { preformEdit.undo() }
        findViewById<View>(R.id.edit_redo).onClick { preformEdit.redo() }
        // 初始化脚本的运行/停止事件
        initScriptRunnableEvent()
        // 设置保存事件
        findViewById<View>(R.id.edit_save).onClick { saveFile() }
        // 初始化菜单列表
        initMenuList()
        // 初始化代码提示栏
        initCodeTipList()
    }

    /**
     * 加载脚本
     */
    @SuppressLint("SetTextI18n")
    private fun loadScript(){
        preformEdit = PreformEdit(codePane.codeText);
        val loadingDialog = CommonUtils.loadingDialog(this)
        doAsync {
            val code = scriptFile.readText()
            runOnUiThread{
                preformEdit.setDefaultText(code);
                loadingDialog.dismiss()
                initScriptTextChangedEvent()
            }
        }
    }

    /**
     * 初始化脚本文字改变事件
     */
    private fun initScriptTextChangedEvent(){
        // 文字改变时添加 *,并且设置用于代码提示的符合片段
        codePane.codeText.doOnTextChanged{ _, _, _, _ ->
            if(!editTitle.text.endsWith('*')){
                editTitle.text = editTitle.text.toString() + "*"
            }
            isSaveFile = false
            // 设置代码提示片段
            if(codePane.codeText.text!!.isNotEmpty()){
                val codeSegment= findCodeSegmentToTip()
                val adapter = codeTipRecyclerView.adapter as TextViewListAdapter
                adapter.datas = JsRuntime.codeTipMap
                    .map { it.key }
                    .filter { it.startsWith(codeSegment) }
                    .toTypedArray()
                codeTipRecyclerView.adapter = adapter
            }
        }
    }

    /**
     * 初始化脚本的运行或停止事件
     */
    private fun initScriptRunnableEvent(){
        // 订阅脚本的运行/停止事件
        GlobalData.jsRunningMapChanged.add(this::listenScriptIsRunning)
        // 根据脚本是否允许设置运行/停止按钮的背景图片
        if(GlobalData.getRunningScriptMap().containsKey(scriptFile.name)){
            runScriptBtn.setImageResource(R.drawable.stop)
        }else{
            runScriptBtn.setImageResource(R.drawable.run)
        }
        // 单击触发运行或停止脚本
        runScriptBtn.onClick {
            if(GlobalData.getRunningScriptMap().containsKey(scriptFile.name)){
                GlobalData.getRunningScriptMap()[scriptFile.name]?.forceExit()
            }else{
                JsRuntime(scriptFile.name, this@EditActivity, codePane.codeText!!.text.toString()).start()
                XToast.success(this@EditActivity, "正在运行：${scriptFile.name}!").show()
            }
        }
        // 订阅脚本运行发生错误的事件
        GlobalData.jsRunningError = { name, info ->
            uiHandler.post {
                if(name == scriptFile.name){
                    CookieBar.builder(this)
                        .setTitle("${scriptFile.name}运行异常")
                        .setMessage(info)
                        .setLayoutGravity(Gravity.BOTTOM)
                        .setAction("详情"){ startActivity(Intent(this, LoggerActivity::class.java)) }
                        .setActionColor(R.color.red)
                        .setDuration(5000)
                        .show()
                }
            }
        }
    }

    /**
     * 监听该脚本是否正在运行
     * @param type  事件类型
     */
    private fun listenScriptIsRunning(type: ScriptRunNumChangeType, scriptName: String){
        if(scriptName != scriptFile.name) return
        if(type == ScriptRunNumChangeType.Start){
            runScriptBtn.setImageResource(R.drawable.stop)
        }else{
            runScriptBtn.setImageResource(R.drawable.run)
        }
    }

    /**
     * 保存文件
     */
    private fun saveFile(){
        runOnUiThread {
            scriptFile.writeText(codePane.codeText!!.text.toString())
            runOnUiThread {
                val title = editTitle.text.toString()
                if(title.endsWith('*')){
                    editTitle.text = title.removeSuffix("*")
                }
                isSaveFile = true
                XToast.success(this, "保存成功").show()
            }
        }
    }

    /**
     * 点击下方符号中的指定符号
     */
    private fun clickSymbol(symbol: String){
        val pos = Math.max(codePane.codeText.selectionStart, 0)
        codePane.codeText.text!!.insert(pos, symbol)
    }

    /**
     * 初始化 代码字符提示列表  如： ；,（ {等
     */
    private fun initSymbolList(){
        val recyclerViw = findViewById<RecyclerView>(R.id.edit_recyclerSymbolView)
        val symbols = "\"()=;/{}!|&_[]+-<>\\*?".toCharArray().map { it.toString() }.toTypedArray()
        val viewWidth =DensityUtils.getScreenSize(true).x / 12
        val linearLayoutManager = LinearLayoutManager(this)
        linearLayoutManager.orientation = LinearLayoutManager.HORIZONTAL
        recyclerViw.layoutManager = linearLayoutManager
        val textViewListAdapter = TextViewListAdapter(this, symbols, viewWidth, -1)
        textViewListAdapter.clickItem = { _, view -> clickSymbol(view.text.toString())}
        recyclerViw.adapter = textViewListAdapter
    }

    /**
     * 获取一段合适的代码片段用于用户获取提示
     */
    private fun findCodeSegmentToTip(): String{
        val codeText: Editable = codePane.codeText.text!!
        val cursorIndex = codePane.codeText.selectionStart
        val stopCharArr = ";(){}<>/!|&=\\+-%*? \n\t".toCharArray()
        for (index in Math.max(0, cursorIndex - 1) downTo 0){
            if(stopCharArr.any { it == codeText[index] }){
                mCodeTipStartIndex = Math.min(index + 1, cursorIndex)
                return codeText.substring(mCodeTipStartIndex, cursorIndex)
            }
        }
        mCodeTipStartIndex = 0
        return codeText.substring(0, cursorIndex)
    }

    /**
     * 点击了代码片段提示
     * @param 选择的代码片段
     */
    private fun clickCodeTip(code: String){
        val codeText: Editable = codePane.codeText.text!!
        val cursorIndex = Math.max(codePane.codeText.selectionStart, 0)
        val len = cursorIndex - mCodeTipStartIndex + 1
        codeText.insert(cursorIndex, code.substring(len - 1))
    }

    /**
     * 初始化代码提示列表
     */
    private fun initCodeTipList(){
        var codeTipArrays = emptyArray<String>()
        val linearLayoutManager = LinearLayoutManager(this)
        linearLayoutManager.orientation = LinearLayoutManager.HORIZONTAL
        codeTipRecyclerView.layoutManager = linearLayoutManager
        val textViewListAdapter = TextViewListAdapter(this, codeTipArrays, -2, -1)
        textViewListAdapter.clickItem = { _, view -> clickCodeTip(view.text.toString())}
        codeTipRecyclerView.adapter = textViewListAdapter
    }

    /**
     * 用户选择菜单栏
     */
    private fun clickMenuItem(index: Int, text: String) {
        when(index){
            // 清空代码
            0 -> codePane.codeText.text!!.clear()
            // 跳转行首
            1 -> codePane.codeText.setSelection(getLineStart(codePane.codeText.selectLine))
            // 跳转行尾
            2 -> codePane.codeText.setSelection(getLineStart(codePane.codeText.selectLine + 1) - 1)
            // 格式化代码
            3 -> formatCode()
            // 加解密文件
            4 -> encryptScript()
            // 查看日志
            5 -> startActivity(Intent(this, LoggerActivity::class.java))
        }
    }

    /**
     * 加解密脚本(异或)
     */
    private fun encryptScript(){
        CommonUtils.inputDialog(this, content = "请输入您的秘钥",
            cancelable = true,
            inputRange = 1..32,
            positiveText = "加密",
            onPositive = {
                var byteArray = codePane.codeText.text.toString().toByteArray()
                byteArray = CommonUtils.xorEncrypt(byteArray, it)
                codePane.codeText.setText(Base64.encodeToString(byteArray, Base64.DEFAULT))
            },
            negativeText = "解密",
            onNegative = {
                val text = codePane.codeText.text.toString()
                try {
                    var byteArray = Base64.decode(text, Base64.DEFAULT)
                    byteArray = CommonUtils.xorEncrypt(byteArray, it)
                    codePane.codeText.setText(String(byteArray))
                } catch (e: Exception) {
                    XToast.error(this, "解密失败!").show()
                }
            }
        )
    }

    /**
     * 根据行号获取行首索引
     */
    private fun getLineStart(lineIndex: Int):Int{
        var index = 0
        var textIndex = 0
        for (line in codePane.codeText.text!!.lines()) {
            if(index < lineIndex){
                textIndex += (line.length + 1)
                index++
            }else{
                break
            }
        }
        return textIndex
    }

    /**
     * 初始化菜单列表
     */
    private fun initMenuList(){
        val menuList = ScaleListView(this)
        menuList.layoutParams = ViewGroup.LayoutParams(-1, -1)
        menuList.backgroundColor = Color.WHITE
        val popupWindow = PopupWindow(menuList, DensityUtils.dp2px(200F), -2)
        popupWindow.animationStyle = R.style.popup_anim
        popupWindow.isOutsideTouchable = true
        popupWindow.elevation = DensityUtils.dp2px(15F).toFloat()
        // 菜单点击显示listView
        findViewById<View>(R.id.edit_menu).onClick {
            popupWindow.showAtLocation(
                findViewById(R.id.edit_root),
                Gravity.RIGHT or Gravity.TOP,
                0,
                0
            )
        }
        val options = arrayOf("清空代码", "跳转行首", "跳转行尾", "格式化代码", "加解密文件", "查看日志")
        menuList.adapter = object : BaseAdapter() {
            override fun getCount(): Int {
                return options.size
            }

            override fun getItem(position: Int): Any {
                return options[position]
            }

            override fun getItemId(position: Int): Long {
                return position.toLong()
            }

            override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
                if(convertView == null){
                    val tv = TextView(this@EditActivity)
                    tv.text = options[position]
                    tv.setTextColor(Color.BLACK)
                    tv.textSize = 16F
                    tv.padding = 40
                    tv.layoutParams = ViewGroup.LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT
                    )
                    tv.onClick {
                        clickMenuItem(position, options[position])
                        popupWindow.dismiss()
                    }
                    return tv
                }
                return convertView!!
            }

        }
    }

    /**
     * 退出编辑器
     */
    private fun backEditor(){
        if(!isSaveFile){
            MaterialDialog.Builder(this)
                .title("警告")
                .iconRes(R.drawable.warning)
                .content("您还未保存，确定退出吗?")
                .cancelable(false)
                .positiveText("确定退出")
                .onPositive { _, _ -> finish() }
                .negativeText("继续编辑")
                .neutralText("保存并退出")
                .onNeutral { _, _ ->
                    saveFile()
                    finish()
                }
                .show()
        }else{
            finish()
        }
    }

    /**
     * 格式化代码
     */
    private fun formatCode() {
        val loadingDialog = CommonUtils.loadingDialog(this, content = "格式化中...")
        doAsync {
            val bytes = assets.open("editor/beautify.js").readBytes()
            val jsEngine = JsEngine()
            jsEngine.reset()
            jsEngine.exec(String(bytes))
            val formatCode = jsEngine.callJsFunc("js_beautify", codePane.codeText!!.text.toString()) as String
            jsEngine.exit()
            runOnUiThread {
                codePane.codeText.setText(formatCode)
                isSaveFile = false
                loadingDialog.dismiss()
            }
        }
    }

    override fun onDestroy() {
        GlobalData.jsRunningMapChanged.remove(this::listenScriptIsRunning)
        super.onDestroy()
    }

    override fun onBackPressed() {
        backEditor()
    }

}